// ml278
// Copyright (C) 2008 Bjørn Øivind Bjørnsen, Aleksander Gjermundsen, Knut Andre Tidemann, Thomas Hansen
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

#include "planetaryzonerenderable.h"
#include "planetaryoptions.h"
#include "planetaryscenemanager.h"
#include "planetarycommon.h"
#include "planetaryheightmapdata.h"
#include "planetaryindexbuffermanager.h"
#include "planetaryzone.h"
#include "planetaryplanet.h"
#include <Ogre.h>

namespace Ogre
{
	String PlanetaryZoneRenderable::mType = "PlanetaryZoneRenderable";

	/**
	 * Constructor
	 * sets initial values
	*/
	PlanetaryZoneRenderable::PlanetaryZoneRenderable(PlanetarySceneManager *mgr) : Renderable(), MovableObject()
	{
		mSceneMgr = mgr;
		mOptions = mgr->getOptions();

		mScale = mOptions->mPlanetRadius;

		mVertices = 0;
		mIndices = 0;

		// FIXME: Read this from init aswell
		mPlanet = 0;

		mLightListDirty = true;
		mIsLoaded = false;
		mIsInit = false;
	}

	/**
	 * Destructor
	 * unloads and uninints the zonerenderable
	*/
	PlanetaryZoneRenderable::~PlanetaryZoneRenderable()
	{
		if(mIsLoaded)
			unload();

		if(mIsInit)
			uninit();
	}
	
	/**
	 * initialises the zone renderable.
	 * sets the default material, sets up the vertex and index buffers
	 * @param zone index of the zone this renderable belongs to
	 * @param parent reference to the zone this renderable belongs to
	*/
	void PlanetaryZoneRenderable::init(unsigned int zone,Ogre::PlanetaryZone *parent)
	{
		assert(!mIsInit);

		mZone	= zone;

		mParent = parent;

		mCastShadows = false;

		const unsigned int rowlength = mOptions->mZoneSizeLOD1;

		// default material, assumes that the planet has an atmosphere
		mMaterial = MaterialManager::getSingleton().getByName(mOptions->mPlanetMaterial + "_groundFromSpace");

		// Default query flags to top bit so users can exclude it if they wish
		MovableObject::mQueryFlags = SceneManager::WORLD_GEOMETRY_TYPE_MASK;
		MovableObject::setRenderQueueGroup(mSceneMgr->getWorldGeometryRenderQueue());

		mVertices = new VertexData();
		mVertices->vertexStart = 0;
		mVertices->vertexCount = rowlength * rowlength;

		VertexDeclaration *decl = mVertices->vertexDeclaration;

		size_t offset = 0;
		decl->addElement(0,0,VET_FLOAT3,VES_POSITION);
		offset += VertexElement::getTypeSize(VET_FLOAT3);
		decl->addElement(0,offset,VET_FLOAT3, VES_NORMAL);
		offset += VertexElement::getTypeSize(VET_FLOAT3);
		decl->addElement(0,offset,VET_FLOAT2, VES_TEXTURE_COORDINATES);
		offset += VertexElement::getTypeSize(VET_FLOAT2);

		mIndices = new IndexData();
		mIndices->indexStart = 0;
		mIndices->indexCount = mSceneMgr->getIndexBufferManager()->getIndexBufferSize(INDEX_BUFFER_ZONE);

		mIsInit = true;
	}

	/**
	 * loads this zone-renderable
	 * fills the vertex buffer, sets the bounds and the material.
	*/
	void PlanetaryZoneRenderable::load()
	{
		mBounds.setNull();

		VertexDeclaration *decl = mVertices->vertexDeclaration;
		VertexBufferBinding *bind = mVertices->vertexBufferBinding;

		// Setup vertices
		HardwareVertexBufferSharedPtr vbuf = 
			HardwareBufferManager::getSingleton().createVertexBuffer(
				decl->getVertexSize(0),
				mVertices->vertexCount,
				HardwareBuffer::HBU_STATIC_WRITE_ONLY);

		bind->setBinding(0,vbuf);

		const VertexElement* poselem = decl->findElementBySemantic(VES_POSITION);

		unsigned int vertexSize = vbuf->getVertexSize();

		uchar *pBuffer = (uchar*)vbuf->lock(HardwareBuffer::HBL_DISCARD);

		switch(mZone)
		{
		case ZONE_UP:
			loadUp(poselem,pBuffer,vertexSize);
			break;
		case ZONE_DOWN:
			loadDown(poselem,pBuffer,vertexSize);
			break;
		case ZONE_FRONT:
			loadFront(poselem,pBuffer,vertexSize);
			break;
		case ZONE_BACK:
			loadBack(poselem,pBuffer,vertexSize);
			break;
		case ZONE_LEFT:
			loadLeft(poselem,pBuffer,vertexSize);
			break;
		case ZONE_RIGHT:
			loadRight(poselem,pBuffer,vertexSize);
			break;
		}

		vbuf->unlock();

		// Setup indices
		// TODO: This is identical for every tile, I think, so move this somewhere else to save space
		mIndices->indexBuffer = mSceneMgr->getIndexBufferManager()->getIndexBuffer(INDEX_BUFFER_ZONE);


		float x = (mBounds.getMaximum().x - mBounds.getMinimum().x);
		float y = (mBounds.getMaximum().y - mBounds.getMinimum().y);
		float z = (mBounds.getMaximum().y - mBounds.getMinimum().y);

		mCenter = Vector3(mBounds.getMinimum().x + (x/2.0f),mBounds.getMinimum().y + (y/2.0f),mBounds.getMinimum().z + (z/2.0f));

		mBoundingRadius = Math::Sqrt(Math::Sqr(x) + Math::Sqr(y) + Math::Sqr(z)) / 2.0f;
		
		// this sets the shader parametres too
		setMaterial(mMaterial);

		mIsLoaded = true;
	}

	/**
	 * If this zone renderable belongs to the zone placed on the upper side of the planet (of the six different locations),
	 * this is called to fill the vertexbuffer.
	 * This loads data from the zone heightmaps and applies deformation based on the information within
	 * @param poselem pointer to the position element of the vertex
	 * @param pBuffer pointer to the locked vertex buffer
	 * @param vertexSize the size of the vertexbuffer
	*/
	void PlanetaryZoneRenderable::loadUp(const VertexElement* poselem, uchar *pBuffer,unsigned int vertexSize)
	{
		const PlanetaryHeightmapData *hdata = mSceneMgr->getHeightmapData();
		const PlanetaryZoneHeightmap *heightmap = hdata->getZoneHeightmap(mPlanet,mZone);
		const unsigned int rowlength = mOptions->mZoneSizeLOD1;

		float *pPos;

		for(unsigned int i = 0; i < rowlength; i++)
		{
			for(unsigned int j = 0; j < rowlength; j++)
			{
				const float *fh = heightmap->getRow(i);

				float x = (2.0f * static_cast<float>(j)/static_cast<float>(rowlength - 1)) - 1.0f;

				float y = 1.0f;

				float z = (2.0f * static_cast<float>(i)/static_cast<float>(rowlength - 1)) - 1.0f;

				float hscale = 1.0f + (((fh[j] - 32768) / 65536) * mOptions->mTerrainScale);

				Vector3 pos = cubeToSphereCoords(x,y,z) * mScale * hscale;

				poselem->baseVertexPointerToElement(pBuffer, &pPos);

				mBounds.merge(pos);

				// Vertex coords
				*pPos++ = pos.x; // X
				*pPos++ = pos.y; // Y
				*pPos++ = pos.z; // Z

				// Normals, dear God help me
				bool nonormal = false;
				Vector3 left, down;

				if(j == 0)
				{
					nonormal = true;
				}
				else
				{
					left.x = (2.0f * static_cast<float>(j - 1)/static_cast<float>(rowlength - 1)) - 1.0f;
					left.y = y;
					left.z = z;
					hscale = 1.0f + (((fh[j-1] - 32768) / 65536) * mOptions->mTerrainScale);
					left = cubeToSphereCoords(left.x,left.y,left.z) * mScale * hscale;
				}


				if(i == rowlength - 1)
				{
					nonormal = true;
				}
				else
				{
					down.x = x;
					down.y = y;
					down.z = (2.0f * static_cast<float>(i + 1)/static_cast<float>(rowlength - 1)) - 1.0f;
					fh = heightmap->getRow(i + 1);
					hscale = 1.0f + (((fh[j] - 32768) / 65536) * mOptions->mTerrainScale);
					down = cubeToSphereCoords(down.x,down.y,down.z) * mScale * hscale;
				}
				
				Vector3 norm;
				
				if(nonormal)
				{
					norm = pos;	
				}
				else
				{
					left = left - pos;
	
					down = down - pos;
	
					left.normalise();
					down.normalise();
					
					norm = left.crossProduct( down );
				}

				
				norm.normalise();

				*pPos++ = norm.x; // X
				*pPos++ = norm.y; // Y
				*pPos++ = norm.z; // Z

				// Texture Coords
				Vector2 tex(((x + 1.0f) / 2), ((z + 1.0f) / 2));
				*pPos++ = tex.x;
				*pPos++ = tex.y;

				pBuffer += vertexSize;
			}
		}
	}

	/**
	 * If this zone renderable belongs to the zone placed on the lower side of the planet (of the six different locations),
	 * this is called to fill the vertexbuffer.
	 * This loads data from the zone heightmaps and applies deformation based on the information within
	 * @param poselem pointer to the position element of the vertex
	 * @param pBuffer pointer to the locked vertex buffer
	 * @param vertexSize the size of the vertexbuffer
	*/
	void PlanetaryZoneRenderable::loadDown(const VertexElement* poselem, uchar *pBuffer,unsigned int vertexSize)
	{
		const PlanetaryHeightmapData *hdata = mSceneMgr->getHeightmapData();
		const PlanetaryZoneHeightmap *heightmap = hdata->getZoneHeightmap(mPlanet,mZone);
		const unsigned int rowlength = mOptions->mZoneSizeLOD1;

		float *pPos;

		for(unsigned int i = 0; i < rowlength; i++)
		{
			for(unsigned int j = 0; j < rowlength; j++)
			{
				const float *fh = heightmap->getRow(i);
				
				float x = (2.0f * static_cast<float>(j)/static_cast<float>(rowlength - 1)) - 1.0f;

				float y = -1.0f;

				float z = (-2.0f * static_cast<float>(i)/static_cast<float>(rowlength - 1)) + 1.0f;

				float hscale = 1.0f + (((fh[j] - 32768) / 65536) * mOptions->mTerrainScale);

				Vector3 pos = cubeToSphereCoords(x,y,z) * mScale * hscale;

				poselem->baseVertexPointerToElement(pBuffer, &pPos);

				mBounds.merge(pos);

				// Vertex coords
				*pPos++ = pos.x; // X
				*pPos++ = pos.y; // Y
				*pPos++ = pos.z; // Z

				// Normals, dear God help me
				bool nonormal = false;
				Vector3 left, down;

				if(j == 0)
				{
					nonormal = true;
				}
				else
				{
					left.x = (2.0f * static_cast<float>(j-1)/static_cast<float>(rowlength - 1)) - 1.0f;;
					left.y = y;
					left.z = z;
					hscale = 1.0f + (((fh[j-1] - 32768) / 65536) * mOptions->mTerrainScale);
					left = cubeToSphereCoords(left.x,left.y,left.z) * mScale * hscale;
				}


				if(i == rowlength - 1)
				{
					nonormal = true;
				}
				else
				{
					down.x = x;
					down.y = y;
					down.z = (-2.0f * static_cast<float>(i+1)/static_cast<float>(rowlength - 1)) + 1.0f;;
					fh = heightmap->getRow(i + 1);
					hscale = 1.0f + (((fh[j] - 32768) / 65536) * mOptions->mTerrainScale);
					down = cubeToSphereCoords(down.x,down.y,down.z) * mScale * hscale;
				}
				
				Vector3 norm;
				
				if(nonormal)
				{
					norm = pos;	
				}
				else
				{
					left = left - pos;
	
					down = down - pos;
	
					left.normalise();
					down.normalise();
					
					norm = left.crossProduct( down );
				}

				
				norm.normalise();
				*pPos++ = norm.x; // X
				*pPos++ = norm.y; // Y
				*pPos++ = norm.z; // Z

				// Texture Coords
				*pPos++ = ((x + 1.0f) / 2);
				*pPos++ = ((z + 1.0f) / 2);

				pBuffer += vertexSize;
			}
		}
	}

	/**
	 * If this zone renderable belongs to the zone placed on the front side of the planet (of the six different locations),
	 * this is called to fill the vertexbuffer.
	 * This loads data from the zone heightmaps and applies deformation based on the information within
	 * @param poselem pointer to the position element of the vertex
	 * @param pBuffer pointer to the locked vertex buffer
	 * @param vertexSize the size of the vertexbuffer
	*/
	void PlanetaryZoneRenderable::loadFront(const VertexElement* poselem, uchar *pBuffer,unsigned int vertexSize)
	{
		const PlanetaryHeightmapData *hdata = mSceneMgr->getHeightmapData();
		const PlanetaryZoneHeightmap *heightmap = hdata->getZoneHeightmap(mPlanet,mZone);
		const unsigned int rowlength = mOptions->mZoneSizeLOD1;

		float *pPos;

		for(unsigned int i = 0; i < rowlength; i++)
		{

			for(unsigned int j = 0; j < rowlength; j++)
			{
				const float *fh = heightmap->getRow(i);
				
				float x = (2.0f * static_cast<float>(j)/static_cast<float>(rowlength - 1)) - 1.0f;

				float y = (-2.0f * static_cast<float>(i)/static_cast<float>(rowlength - 1)) + 1.0f;

				float z = 1.0f;

				float hscale = 1.0f + (((fh[j] - 32768) / 65536) * mOptions->mTerrainScale);

				Vector3 pos = cubeToSphereCoords(x,y,z) * mScale * hscale;

				poselem->baseVertexPointerToElement(pBuffer, &pPos);

				mBounds.merge(pos);

				// Vertex coords
				*pPos++ = pos.x; // X
				*pPos++ = pos.y; // Y
				*pPos++ = pos.z; // Z

				// Normals, dear God help me
				bool nonormal = false;
				Vector3 left, down;

				if(j == 0)
				{
					nonormal = true;
				}
				else
				{
					left.x = (2.0f * static_cast<float>(j - 1)/static_cast<float>(rowlength - 1)) - 1.0f;
					left.y = y;
					left.z = z;
					hscale = 1.0f + (((fh[j-1] - 32768) / 65536) * mOptions->mTerrainScale);
					left = cubeToSphereCoords(left.x,left.y,left.z) * mScale * hscale;
				}


				if(i == rowlength - 1)
				{
					nonormal = true;
				}
				else
				{
					down.x = x;
					down.y = (-2.0f * static_cast<float>(i + 1)/static_cast<float>(rowlength - 1)) + 1.0f;
					down.z = z;
					fh = heightmap->getRow(i + 1);
					hscale = 1.0f + (((fh[j] - 32768) / 65536) * mOptions->mTerrainScale);
					down = cubeToSphereCoords(down.x,down.y,down.z) * mScale * hscale;
				}
				
				Vector3 norm;
				
				if(nonormal)
				{
					norm = pos;	
				}
				else
				{
					left = left - pos;
	
					down = down - pos;
	
					left.normalise();
					down.normalise();
					
					norm = left.crossProduct( down );
				}

				
				norm.normalise();
				*pPos++ = norm.x; // X
				*pPos++ = norm.y; // Y
				*pPos++ = norm.z; // Z

				// Texture Coords
				*pPos++ = ((x + 1.0f) / 2);
				*pPos++ = ((y + 1.0f) / 2);

				pBuffer += vertexSize;
			}
		}
	}

	/**
	 * If this zone renderable belongs to the zone placed on the back side of the planet (of the six different locations),
	 * this is called to fill the vertexbuffer.
	 * This loads data from the zone heightmaps and applies deformation based on the information within
	 * @param poselem pointer to the position element of the vertex
	 * @param pBuffer pointer to the locked vertex buffer
	 * @param vertexSize the size of the vertexbuffer
	*/
	void PlanetaryZoneRenderable::loadBack(const VertexElement* poselem, uchar *pBuffer,unsigned int vertexSize)
	{
		const PlanetaryHeightmapData *hdata = mSceneMgr->getHeightmapData();
		const PlanetaryZoneHeightmap *heightmap = hdata->getZoneHeightmap(mPlanet,mZone);
		const unsigned int rowlength = mOptions->mZoneSizeLOD1;

		float *pPos;

		for(unsigned int i = 0; i < rowlength; i++)
		{
			for(unsigned int j = 0; j < rowlength; j++)
			{
				const float *fh = heightmap->getRow(i);
				
				float x = (2.0f * static_cast<float>(j)/static_cast<float>(rowlength - 1)) - 1.0f;

				float y = (2.0f * static_cast<float>(i)/static_cast<float>(rowlength - 1)) - 1.0f;

				float z = -1.0f;

				float hscale = 1.0f + (((fh[j] - 32768) / 65536) * mOptions->mTerrainScale);

				Vector3 pos = cubeToSphereCoords(x,y,z) * mScale * hscale;

				poselem->baseVertexPointerToElement(pBuffer, &pPos);

				mBounds.merge(pos);

				// Vertex coords
				*pPos++ = pos.x; // X
				*pPos++ = pos.y; // Y
				*pPos++ = pos.z; // Z

				// Normals, dear God help me
				bool nonormal = false;
				Vector3 left, down;

				if(j == 0)
				{
					nonormal = true;
				}
				else
				{
					left.x = (2.0f * static_cast<float>(j-1)/static_cast<float>(rowlength - 1)) - 1.0f;
					left.y = y;
					left.z = z;
					hscale = 1.0f + (((fh[j-1] - 32768) / 65536) * mOptions->mTerrainScale);
					left = cubeToSphereCoords(left.x,left.y,left.z) * mScale * hscale;
				}


				if(i == rowlength - 1)
				{
					nonormal = true;
				}
				else
				{
					down.x = x;
					down.y = (2.0f * static_cast<float>(i + 1)/static_cast<float>(rowlength - 1)) - 1.0f;
					down.z = z;
					fh = heightmap->getRow(i + 1);
					hscale = 1.0f + (((fh[j] - 32768) / 65536) * mOptions->mTerrainScale);
					down = cubeToSphereCoords(down.x,down.y,down.z) * mScale * hscale;
				}
				
				Vector3 norm;
				
				if(nonormal)
				{
					norm = pos;	
				}
				else
				{
					left = left - pos;
	
					down = down - pos;
	
					left.normalise();
					down.normalise();
					
					norm = left.crossProduct( down );
				}

				
				norm.normalise();
				*pPos++ = norm.x; // X
				*pPos++ = norm.y; // Y
				*pPos++ = norm.z; // Z

				// Texture Coords
				*pPos++ = ((x + 1.0f) / 2);
				*pPos++ = ((y + 1.0f) / 2);

				pBuffer += vertexSize;
			}
		}
	}

	/**
	 * If this zone renderable belongs to the zone placed on the left side of the planet (of the six different locations),
	 * this is called to fill the vertexbuffer.
	 * This loads data from the zone heightmaps and applies deformation based on the information within
	 * @param poselem pointer to the position element of the vertex
	 * @param pBuffer pointer to the locked vertex buffer
	 * @param vertexSize the size of the vertexbuffer
	*/
	void PlanetaryZoneRenderable::loadLeft(const VertexElement* poselem, uchar *pBuffer,unsigned int vertexSize)
	{
		const PlanetaryHeightmapData *hdata = mSceneMgr->getHeightmapData();
		const PlanetaryZoneHeightmap *heightmap = hdata->getZoneHeightmap(mPlanet,mZone);
		const unsigned int rowlength = mOptions->mZoneSizeLOD1;

		float *pPos;

		for(unsigned int i = 0; i < rowlength; i++)
		{
			for(unsigned int j = 0; j < rowlength; j++)
			{
				const float *fh = heightmap->getRow(i);
				
				float x = -1.0f;

				float y = (-2.0f * static_cast<float>(i)/static_cast<float>(rowlength - 1)) + 1.0f;

				float z = (2.0f * static_cast<float>(j)/static_cast<float>(rowlength - 1)) - 1.0f;

				float hscale = 1.0f + (((fh[j] - 32768) / 65536) * mOptions->mTerrainScale);

				Vector3 pos = cubeToSphereCoords(x,y,z) * mScale * hscale;

				poselem->baseVertexPointerToElement(pBuffer, &pPos);

				mBounds.merge(pos);

				// Vertex coords
				*pPos++ = pos.x; // X
				*pPos++ = pos.y; // Y
				*pPos++ = pos.z; // Z

				// Normals, dear God help me
				bool nonormal = false;
				Vector3 left, down;

				if(j == 0)
				{
					nonormal = true;
				}
				else
				{
					left.x = x;
					left.y = y;
					left.z = (2.0f * static_cast<float>(j-1)/static_cast<float>(rowlength - 1)) - 1.0f;
					hscale = 1.0f + (((fh[j-1] - 32768) / 65536) * mOptions->mTerrainScale);
					left = cubeToSphereCoords(left.x,left.y,left.z) * mScale * hscale;
				}


				if(i == rowlength - 1)
				{
					nonormal = true;
				}
				else
				{
					down.x = x;
					down.y = (-2.0f * static_cast<float>(i+1)/static_cast<float>(rowlength - 1)) + 1.0f;
					down.z = z;
					fh = heightmap->getRow(i + 1);
					hscale = 1.0f + (((fh[j] - 32768) / 65536) * mOptions->mTerrainScale);
					down = cubeToSphereCoords(down.x,down.y,down.z) * mScale * hscale;
				}
				
				Vector3 norm;
				
				if(nonormal)
				{
					norm = pos;	
				}
				else
				{
					left = left - pos;
	
					down = down - pos;
	
					left.normalise();
					down.normalise();
					
					norm = left.crossProduct( down );
				}

				
				norm.normalise();
				*pPos++ = norm.x; // X
				*pPos++ = norm.y; // Y
				*pPos++ = norm.z; // Z

				// Texture Coords
				*pPos++ = ((z + 1.0f) / 2);
				*pPos++ = ((y + 1.0f) / 2);

				pBuffer += vertexSize;
			}
		}
	}

	/**
	 * If this zone renderable belongs to the zone placed on the right side of the planet (of the six different locations),
	 * this is called to fill the vertexbuffer.
	 * This loads data from the zone heightmaps and applies deformation based on the information within
	 * @param poselem pointer to the position element of the vertex
	 * @param pBuffer pointer to the locked vertex buffer
	 * @param vertexSize the size of the vertexbuffer
	*/
	void PlanetaryZoneRenderable::loadRight(const VertexElement* poselem, uchar *pBuffer,unsigned int vertexSize)
	{
		const PlanetaryHeightmapData *hdata = mSceneMgr->getHeightmapData();
		const PlanetaryZoneHeightmap *heightmap = hdata->getZoneHeightmap(mPlanet,mZone);
		const unsigned int rowlength = mOptions->mZoneSizeLOD1;

		float *pPos;

		for(unsigned int i = 0; i < rowlength; i++)
		{
			for(unsigned int j = 0; j < rowlength; j++)
			{
				const float *fh = heightmap->getRow(i);
				
				float x = 1.0f;

				float y = (-2.0f * static_cast<float>(i)/static_cast<float>(rowlength - 1)) + 1.0f;

				float z = (-2.0f * static_cast<float>(j)/static_cast<float>(rowlength - 1)) + 1.0f;

				float hscale = 1.0f + (((fh[j] - 32768) / 65536) * mOptions->mTerrainScale);

				Vector3 pos = cubeToSphereCoords(x,y,z) * mScale * hscale;

				poselem->baseVertexPointerToElement(pBuffer, &pPos);

				mBounds.merge(pos);

				// Vertex coords
				*pPos++ = pos.x; // X
				*pPos++ = pos.y; // Y
				*pPos++ = pos.z; // Z

				// Normals, dear God help me
				bool nonormal = false;
				Vector3 left, down;

				if(j == 0)
				{
					nonormal = true;
				}
				else
				{
					left.x = x;
					left.y = y;
					left.z = (-2.0f * static_cast<float>(j-1)/static_cast<float>(rowlength - 1)) + 1.0f;
					hscale = 1.0f + (((fh[j-1] - 32768) / 65536) * mOptions->mTerrainScale);
					left = cubeToSphereCoords(left.x,left.y,left.z) * mScale * hscale;
				}


				if(i == rowlength - 1)
				{
					nonormal = true;
				}
				else
				{
					down.x = x;
					down.y = (-2.0f * static_cast<float>(i+1)/static_cast<float>(rowlength - 1)) + 1.0f;
					down.z = z;
					fh = heightmap->getRow(i + 1);
					hscale = 1.0f + (((fh[j] - 32768) / 65536) * mOptions->mTerrainScale);
					down = cubeToSphereCoords(down.x,down.y,down.z) * mScale * hscale;
				}
				
				Vector3 norm;
				
				if(nonormal)
				{
					norm = pos;	
				}
				else
				{
					left = left - pos;
	
					down = down - pos;
	
					left.normalise();
					down.normalise();
					
					norm = left.crossProduct( down );
				}

				
				norm.normalise();
				*pPos++ = norm.x; // X
				*pPos++ = norm.y; // Y
				*pPos++ = norm.z; // Z

				// Texture Coords
				*pPos++ = ((z + 1.0f) / 2);
				*pPos++ = ((y + 1.0f) / 2);

				pBuffer += vertexSize;
			}
		}
	}

	/**
	 * unloads the zone renderable
	*/
	void PlanetaryZoneRenderable::unload()
	{
		mIsLoaded = false;
	}

	/**
	 * uninits the zone renderable
	*/
	void PlanetaryZoneRenderable::uninit()
	{
		mIsInit = false;
	}
	
	/**
	 * Sets the material for this zone renderable
	 * @param mat the material to set
	 * @note sets some paramatres to the ground shader here
	*/
	void PlanetaryZoneRenderable::setMaterial(MaterialPtr mat)
	{
		mMaterial = mat;
		float minalt = mScale * (1.0f + (((-0.5) * mOptions->mTerrainScale)));

		GpuProgramParametersSharedPtr pParams;
		Ogre::Pass *pPass;
		
		pPass = mMaterial->getTechnique(0)->getPass(0);
		pParams = pPass->getVertexProgramParameters();
		
		pParams->setNamedConstant("vPlanetCentre", Vector3(0,0,0));
		pParams->setNamedConstant("fMaxAltitude", 200.0f);
		pParams->setNamedConstant("fMinAltitudeWithRadius", minalt);
		LogManager::getSingleton().logMessage("MinAlt: " + StringConverter::toString(mScale));
	}

	/**
	 * Overridden from MovableObject.
	 * Sets the visibility of the current zone renderable for the current camera.
	*/
	void PlanetaryZoneRenderable::_notifyCurrentCamera(Camera* cam)
	{
		if(!mIsLoaded || !mIsInit)
		{
			mVisible = false;
			return;
		}

		mVisible = true;
	}

	/**
	 * Overridden from MovableObject.
	 * adds this renderable to the renderqueue.
	*/
	void PlanetaryZoneRenderable::_updateRenderQueue(RenderQueue* queue)
	{
		queue->addRenderable(this);
	}

	/**
	 * Overridden from MovableObject
	*/
	void PlanetaryZoneRenderable::_notifyAttached(Node* parent, bool isTagPoint)
	{
		MovableObject::_notifyAttached(parent, isTagPoint);
	}

	/**
	 * Overridden from MovableObject
	*/
	void PlanetaryZoneRenderable::getRenderOperation(RenderOperation& op)
	{
		op.useIndexes = true;
		op.operationType = RenderOperation::OT_TRIANGLE_LIST;
		op.vertexData = mVertices;
		op.indexData = mIndices;
	}

	/**
	 * Overridden from Renderable
	*/
	void PlanetaryZoneRenderable::getWorldTransforms(Matrix4 *xform) const
	{
		*xform = mParentNode->_getFullTransform();
	}

	/**
	 * Overridden from Renderable
	*/
	const Quaternion &PlanetaryZoneRenderable::getWorldOrientation() const
	{
		return mParentNode->_getDerivedOrientation();
	}

	/**
	 * Overridden from Renderable
	*/
	const Vector3 &PlanetaryZoneRenderable::getWorldPosition() const
	{
		return mCenter;
	}
	
	/**
	 * Overridden from Renderable
	*/
	Real PlanetaryZoneRenderable::getSquaredViewDepth(const Camera *cam) const
	{
		return (PlanetaryZoneRenderable::getWorldPosition() - cam->getDerivedPosition()).squaredLength();
	}

	/**
	 * Overridden from Renderable
	*/
	const LightList &PlanetaryZoneRenderable::getLights() const
	{
		if (mLightListDirty)
		{
			getParentSceneNode()->getCreator()->_populateLightList(
					mCenter, 
					this->getBoundingRadius(), 
					mLightList);
					mLightListDirty = false;
		}
		return mLightList;
	}
}
